#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include <dirent.h>
#include <ifaddrs.h>
#include <netdb.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <linux/rtnetlink.h>
#include "networkUtils.h"

static int if_iface_up(const char *iface_name) {
    char path[256];
    FILE *fp;
    int flags;

    // 构造flags文件的路径
    snprintf(path, sizeof(path), "/sys/class/net/%s/flags", iface_name);

    // 打开文件并读取标志值
    fp = fopen(path, "r");
    if (fp == NULL) {
        return 0;  // 无法打开文件，假定接口未启用
    }

    if (fscanf(fp, "%x", &flags) == 1 && flags & 1) {
        fclose(fp);
        return 1;  // 标志值的最低位为1，接口已启用
    }

    fclose(fp);
    return 0;  // 接口未启用
}

static int if_iface_connected(const char *iface_name) {
    char path[256];
    FILE *fp;
    int flags;

    // 构造flags文件的路径
    snprintf(path, sizeof(path), "/sys/class/net/%s/carrier", iface_name);

    // 打开文件并读取标志值
    fp = fopen(path, "r");
    if (fp == NULL) {
        return 0;  // 无法打开文件，假定接口未连接
    }

    if (fscanf(fp, "%d", &flags) == 1 && flags & 0x1) {
        fclose(fp);
        return 1;  // 标志值的次低位为1，接口已连接
    }

    fclose(fp);
    return 0;  // 接口未连接
}

static int get_iface_mac(const char *iface_name, char *mac) {
    char path[256];
    FILE *fp;

    // 构造address文件的路径
    snprintf(path, sizeof(path), "/sys/class/net/%s/address", iface_name);

    // 打开文件并读取MAC地址
    fp = fopen(path, "r");
    if (fp == NULL) {
        return 0;  // 无法打开文件，读取失败
    }

    if (fscanf(fp, "%s", mac) == 1) {
        fclose(fp);
        return 1;  // 读取成功
    }

    fclose(fp);
    return 0;  // 读取失败
}

static int get_iface_speed(const char *iface_name) {
    char path[256];
    FILE *fp;
    int speed;

    // 构造speed文件的路径
    snprintf(path, sizeof(path), "/sys/class/net/%s/speed", iface_name);

    // 打开文件并读取速度值
    fp = fopen(path, "r");
    if (fp == NULL) {
        return 0;  // 无法打开文件，读取失败
    }

    if (fscanf(fp, "%d", &speed) == 1) {
        fclose(fp);
        return speed;  // 读取成功
    }

    fclose(fp);
    return 0;  // 读取失败
}

static void convertSubnetLengthToMask(int length, char *dstMask, int dstLen) {
    if (dstMask == NULL || dstLen < 16) return;
    memset(dstMask, 0, dstLen);
    strcpy(dstMask, "0.0.0.0");
    if (length < 0 || length > 32) {
        DBGE("Invalid subnet length.\n");
        return;
    }

    uint32_t mask = 0xFFFFFFFF;
    mask <<= (32 - length);

    snprintf(dstMask, dstLen, "%d.%d.%d.%d", (mask >> 24) & 0xFF,
             (mask >> 16) & 0xFF, (mask >> 8) & 0xFF, mask & 0xFF);
}

static int getIPandSubnetMask(const char *interface, char *ip, int ipLen,
                              char *netmask, int maskLen) {
    FILE *fp = NULL;
    char command[256] = {0};
    char line[1024] = {0};
    if (ip == NULL || netmask == NULL || ipLen < 16 || maskLen < 16) return 1;
    memset(ip, 0, ipLen);
    memset(netmask, 0, ipLen);
    strcpy(ip, "0.0.0.0");
    strcpy(netmask, "0.0.0.0");
    snprintf(command, sizeof(command), "ip addr show %s", interface);

    fp = popen(command, "r");
    if (fp == NULL) {
        DBGE("Failed to run command\n");
        return 1;
    }

    while (fgets(line, sizeof(line), fp) != NULL) {
        char *ipAddr;
        if ((ipAddr = strstr(line, "inet ")) != NULL) {
            ipAddr += strlen("inet ");
            char *slash = strchr(ipAddr, '/');
            if (slash != NULL) {
                *slash = '\0';  // Temporarily terminate the IP address string
                DBGI("IP Address: %s\n", ipAddr);
                strcpy(ip, ipAddr);

                char *space = strchr(slash + 1, ' ');
                if (space != NULL) {
                    *space = '\0';  // Temporarily terminate the subnet mask
                                    // length string
                }
                int netmask_len = atoi(slash + 1);
                convertSubnetLengthToMask(netmask_len, netmask, maskLen);
            }
            break;  //
        }
    }
    pclose(fp);
    return 0;
}

static int get_ip_and_netmask(const char *interface_name, char *ipaddr,
                              char *netmask) {
    struct ifaddrs *ifaddr, *ifa;
    int family;
    int find_iface = 0;
    if (ipaddr == NULL || netmask == NULL) return 0;

    if (getifaddrs(&ifaddr) == -1) {
        perror("getifaddrs");
        return 0;
    }

    // 遍历返回的链表
    for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
        if (ifa->ifa_addr == NULL) continue;

        family = ifa->ifa_addr->sa_family;
        if (family == AF_INET && strcmp(ifa->ifa_name, interface_name) == 0) {
            // 获取IP地址
            find_iface = 1;
            if (getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), ipaddr,
                            NI_MAXHOST, NULL, 0, NI_NUMERICHOST) != 0) {
                perror("getnameinfo");
                freeifaddrs(ifaddr);
                return 0;
            }

            // 获取子网掩码
            if (getnameinfo(ifa->ifa_netmask, sizeof(struct sockaddr_in),
                            netmask, NI_MAXHOST, NULL, 0,
                            NI_NUMERICHOST) != 0) {
                perror("getnameinfo");
                freeifaddrs(ifaddr);
                return 0;
            }
            break;  // 找到指定接口后退出循环
        }
    }

    freeifaddrs(ifaddr);
    return find_iface ? 1 : 0;
}

// 检查给定的接口是否是虚拟接口
static int is_virtual_interface(const char *iface_name) {
    char path[256];
    struct stat statbuf;

    // 构造在/sys/devices/virtual/net下的路径
    snprintf(path, sizeof(path), "/sys/devices/virtual/net/%s", iface_name);

    // 使用stat()检查路径是否存在
    if (stat(path, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
        return 1;  // 路径存在且为目录，说明是虚拟接口
    }
    return 0;  // 路径不存在，不是虚拟接口
}

// 检查给定的接口是否是CAN设备
static int is_can_interface(const char *iface_name) {
    char path[256];
    FILE *fp;
    int type;

    // 构造type文件的路径
    snprintf(path, sizeof(path), "/sys/class/net/%s/type", iface_name);

    // 打开文件并读取类型值
    fp = fopen(path, "r");
    if (fp == NULL) {
        return 0;  // 无法打开文件，假定不是CAN设备
    }

    if (fscanf(fp, "%d", &type) == 1 && type == 280) {
        fclose(fp);
        return 1;  // 类型值为280，是CAN设备
    }

    fclose(fp);
    return 0;  // 不是CAN设备
}

static int isDhcpConfigured(const char *interface) {
    char command[256] = {0};
    if (interface == NULL) return 0;
    snprintf(command, sizeof(command), "ip addr show %s", interface);

    FILE *fp = popen(command, "r");
    if (fp == NULL) {
        DBGE("Failed to run command\n");
        return 0;
    }

    char output[1024] = {0};
    int isDynamic = 0;  // Flag to indicate DHCP configuration
    while (fgets(output, sizeof(output), fp) != NULL) {
        if (strstr(output, "dynamic") != NULL) {
            isDynamic = 1;
            break;
        }
    }

    pclose(fp);
    return isDynamic;
}

static int checkInterfaceDhcped(const char *interface) {
    const char *INTERFACE_CONFIG = "/etc/network/interfaces";
    FILE *file = fopen(INTERFACE_CONFIG, "r");
    if (file == NULL) {
        perror("Error opening file");
        return 0;
    }

    char line[256];
    int foundInterface = 0;
    while (fgets(line, sizeof(line), file) != NULL) {
        if (strstr(line, interface) != NULL) {
            foundInterface = 1;
        }

        if (foundInterface) {
            if (strstr(line, "dhcp") != NULL) {
                DBGI("%s is configured via DHCP.\n", interface);
                fclose(file);
                return 1;
            } else if (strstr(line, "static") != NULL) {
                DBGI("%s is configured with a static IP address.\n", interface);
                fclose(file);
                return 0;
            }
        }
    }

    if (!foundInterface) {
        DBGE("Interface %s not found in configuration.\n", interface);
    } else {
        DBGE("Configuration method for %s not found.\n", interface);
    }

    fclose(file);
    return 0;
}

static int getGatewayForInterface(const char *interface, char *gateway,
                                  int gatewayLen) {
    FILE *fp = NULL;
    char line[1024] = {0};
    char command[150] = {0};
    if (gateway == NULL || gatewayLen < 16) return 1;
    // 构造命令，指定要查找的网络接口
    memset(gateway, 0, gatewayLen);
    strcpy(gateway, "0.0.0.0");
    snprintf(command, sizeof(command), "ip route list dev %s", interface);

    // 使用popen执行命令
    fp = popen(command, "r");
    if (fp == NULL) {
        perror("Failed to run command");
        return 1;
    }

    // 逐行读取输出
    while (fgets(line, sizeof(line), fp) != NULL) {
        // 查找包含"default"的行
        if (strstr(line, "default") != NULL) {
            char *gateway_ = strstr(line, "via");
            if (gateway_) {
                gateway_ += 4;  // 移动到网关IP地址的开始位置
                char *end = strstr(gateway_, " ");
                if (end) {
                    *end = '\0';  // 终止字符串，只打印网关地址
                }
                strcpy(gateway, gateway_);
                DBGI("Default Gateway for %s: %s\n", interface, gateway);
                pclose(fp);
                return 1;
            }
        }
    }
    pclose(fp);
    return 0;
}

int getDnsServers(char (*dnsServers)[16], int maxServers) {
    FILE *file;
    int i = 0;
    char line[256];
    int count = 0;
    file = fopen("/etc/resolv.conf", "r");
    if (file == NULL) {
        perror("Failed to open /etc/resolv.conf");
        exit(EXIT_FAILURE);
    }

    while (fgets(line, sizeof(line), file) != NULL && count < maxServers) {
        if (strncmp(line, "nameserver", 10) == 0) {
            // Copy the DNS server address to the array, excluding "nameserver "
            memset(dnsServers[count], 0, 16);
            strncpy(dnsServers[count], line + 11, 16 - 1);
            for (i = strlen(dnsServers[count]) - 1; i > 0; i--) {
                if (dnsServers[count][i] == '\n' ||
                    dnsServers[count][i] == '\r') {
                    dnsServers[count][i] = '\0';
                    continue;
                }
                break;
            }
            // Increment the count of DNS servers found
            count++;
        }
    }

    fclose(file);
    return count;  // Return the number of DNS servers found
}

int list_network_name(char (*iface)[32], int srcDepth) {
    DIR *dir;
    struct dirent *entry;
    char path[] = "/sys/class/net";
    int i = 0;
    dir = opendir(path);
    if (!dir) {
        perror("opendir");
        return 1;
    }

    while ((entry = readdir(dir)) != NULL) {
        // 跳过"."和".."这两个特殊目录
        if (strcmp(entry->d_name, ".") == 0 ||
            strcmp(entry->d_name, "..") == 0) {
            continue;
        }

        // 排除自回环接口lo
        if (strcmp(entry->d_name, "lo") == 0) {
            continue;
        }

        // 检查是否为虚拟接口或CAN设备
        if (!is_virtual_interface(entry->d_name) &&
            !is_can_interface(entry->d_name)) {
            if (i >= srcDepth) return 0;
            if (iface[i] == NULL) {
                printf("iface [%d] NULL\n", i);
                continue;
            }
            if (strlen(entry->d_name) > 8) {
                printf("entry->d_name [%s] length > 32\n", entry->d_name);
                continue;
            }
            memset(iface[i], 0, 32);
            strcpy(iface[i], entry->d_name);
            i++;
        }
    }
    closedir(dir);
    return i;
}

int list_network_interfaces(host_net_info_t *host_net_info) {
    DIR *dir;
    struct dirent *entry;
    char path[] = "/sys/class/net";
    int i = 0;
    if (host_net_info == NULL) return 1;
    dir = opendir(path);
    if (!dir) {
        perror("opendir");
        return 1;
    }

    while ((entry = readdir(dir)) != NULL) {
        // 跳过"."和".."这两个特殊目录
        if (strcmp(entry->d_name, ".") == 0 ||
            strcmp(entry->d_name, "..") == 0) {
            continue;
        }

        // 排除自回环接口lo
        if (strcmp(entry->d_name, "lo") == 0) {
            continue;
        }

        // 检查是否为虚拟接口或CAN设备
        if (!is_virtual_interface(entry->d_name) &&
            !is_can_interface(entry->d_name)) {
            iface_info_t *iface = &(host_net_info->iface[i++]);
            memset(iface, 0, sizeof(iface_info_t));
            if (strlen(entry->d_name) <= sizeof(iface->name))
                strcpy(iface->name, entry->d_name);
            get_iface_mac(entry->d_name, iface->mac);
            iface->iface_up = if_iface_up(entry->d_name);
            iface->phy_connected = if_iface_connected(entry->d_name);
            iface->speed = get_iface_speed(entry->d_name);
            getIPandSubnetMask(entry->d_name, iface->address,
                               sizeof(iface->address), iface->netmask,
                               sizeof(iface->netmask));
            getGatewayForInterface(entry->d_name, iface->gateway,
                                   sizeof(iface->gateway));
            iface->dnsCount = getDnsServers(iface->dns, 2);
            if (iface->iface_up) {
#ifdef __x86_64__
                iface->ifDHCP = isDhcpConfigured(entry->d_name);
#elif __aarch64__
                iface->ifDHCP = checkInterfaceDhcped(entry->d_name);
#else
                iface->ifDHCP = 0;
#endif
            } else {
#ifdef __aarch64__
                iface->ifDHCP = checkInterfaceDhcped(entry->d_name);
#else
                iface->ifDHCP = 0;
#endif
            }
        }
    }
    host_net_info->iface_count = i;
    closedir(dir);
    return 0;
}

void outIfaceInfo(iface_info_t *iface_info) {
    if (iface_info == NULL) {
        DBGI("NULL \n");
        return;
    }
    DBGI("\tInterface: <%s>\n", iface_info->name);

    DBGI("\t    IPv4 Address: <%s>\n", iface_info->address);
    DBGI("\t    Netmask: <%s>\n", iface_info->netmask);

    if (iface_info->phy_connected) {
        DBGI("\t    Speed: <%d>\n", iface_info->speed);
    }
    DBGI("\t    Hardware Address: <%s>\n", iface_info->mac);
    DBGI("\t    Enabled: <%s>\n", (iface_info->iface_up ? "up" : "down"));
    DBGI("\t    Connected: <%s>\n",
         (iface_info->phy_connected ? "connected" : "disconnect"));
    DBGI("\n");
}

int get_hostname(char *hostname, int srclen) {
    char hostname_[HOST_NAME_MAX + 1] = {0};
    if (hostname == NULL || srclen <= 0) return 1;
    memset(hostname_, 0, srclen);
    if (gethostname(hostname_, HOST_NAME_MAX + 1) == 0) {
        int dstlen = strlen(hostname_);
        if (dstlen >= srclen) {
            dstlen = srclen - 1;
        }
        memcpy(hostname, hostname_, dstlen);
        DBGI("Hostname: %s\n", hostname);
        return 0;
    }
    return 1;
}

int find_free_port(int port_base, uint32_t range) 
{
    int port_end = port_base + range;
    if(range == 0 || port_base + range > 65535){
        port_end = 65535;
    }
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        printf("Unable to create socket\n");
        return -1;
    }

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);

    // 尝试绑定端口，从port_base开始
    for (int port = port_base; port < port_end; ++port) {
        addr.sin_port = htons(port);

        // 设置端口重用，这样可以立即重新使用端口
        int optval = 1;
        setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));

        if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) {
            close(sock); // 成功后关闭套接字
            return port; // 返回找到的空闲端口
        }
    }

    close(sock); // 关闭套接字，所有端口都尝试过后
    return -1; // 如果所有端口都被占用，返回-1
}

static int get_default_interface(char *if_name, size_t if_name_len) {
    struct {
        struct nlmsghdr nlh;
        struct rtmsg rtm;
        char buf[8192];
    } req;
    int sock = 0;
    if(if_name == NULL || if_name_len < IFNAMSIZ){
        printf("Invalid arguments\n");
        return -1;
    }
    sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
    if (sock < 0) {
        perror("socket");
        return -1;
    }
    

    memset(&req, 0, sizeof(req));
    req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
    req.nlh.nlmsg_type = RTM_GETROUTE;
    req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
    req.rtm.rtm_family = AF_INET;

    if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0) {
        perror("send");
        close(sock);
        return -1;
    }

    int len;
    struct nlmsghdr *nlmp;
    struct rtmsg *rtmp;
    struct rtattr *rtap;
    int rtalen;

    while ((len = recv(sock, &req, sizeof(req), 0)) > 0) {
        for (nlmp = (struct nlmsghdr *)&req; NLMSG_OK(nlmp, len); nlmp = NLMSG_NEXT(nlmp, len)) {
            if (nlmp->nlmsg_type == NLMSG_DONE) {
                close(sock);
                return -1;
            }

            if (nlmp->nlmsg_type == NLMSG_ERROR) {
                perror("NLMSG_ERROR");
                close(sock);
                return -1;
            }

            rtmp = (struct rtmsg *)NLMSG_DATA(nlmp);
            rtap = (struct rtattr *)RTM_RTA(rtmp);
            rtalen = RTM_PAYLOAD(nlmp);

            for (; RTA_OK(rtap, rtalen); rtap = RTA_NEXT(rtap, rtalen)) {
                if (rtap->rta_type == RTA_OIF) {
                    if_indextoname(*(int *)RTA_DATA(rtap), if_name);
                    close(sock);
                    return 0;
                }
            }
        }
    }
    close(sock);
    return -1;
}

static int get_ip_address(const char *if_name, char *ip_address, size_t ip_address_len) {
    int sock;
    struct ifreq ifr;

    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
        perror("socket");
        return -1;
    }

    strncpy(ifr.ifr_name, if_name, IFNAMSIZ - 1);
    ifr.ifr_name[IFNAMSIZ - 1] = 0;

    if (ioctl(sock, SIOCGIFADDR, &ifr) < 0) {
        perror("ioctl");
        close(sock);
        return -1;
    }

    struct sockaddr_in *ipaddr = (struct sockaddr_in *)&ifr.ifr_addr;
    strncpy(ip_address, inet_ntoa(ipaddr->sin_addr), ip_address_len - 1);
    ip_address[ip_address_len - 1] = 0;

    close(sock);
    return 0;
}

int get_local_ip(char *local_ip, size_t srclen)
{
    char if_name[IFNAMSIZ] = {0};
    char ip_address[INET_ADDRSTRLEN];
    if(local_ip == NULL || srclen < INET_ADDRSTRLEN){
        printf("Invalid arguments\n");
        return -1;
    }
    if (get_default_interface(if_name, sizeof(if_name)) < 0) {
        fprintf(stderr, "Failed to get default interface\n");
        return -1;
    }

    printf("Default interface: %s\n", if_name);

    if (get_ip_address(if_name, ip_address, sizeof(ip_address)) < 0) {
        fprintf(stderr, "Failed to get IP address for interface %s\n", if_name);
        return -1;
    }
    memset(local_ip, 0, srclen);
    strcpy(local_ip, ip_address);
    return 0;
}
